/*
 * Copyright 2011 CraneLeeon authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.crane.webtemplate.dao;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.crane.webtemplate.model.BO;
import org.hibernate.Query;

/**
 *
 * @author CraneLeeon
 */
public abstract class BaseDAO implements IDAO {

    public abstract String getBOName();

    public abstract String getBOSimpleName();

    @Override
    public Long create(BO bo) throws SQLException {
        return (Long) HibernateUtil.getCurrentSession().save(bo);
    }

    @Override
    public void update(BO bo) throws SQLException {
        HibernateUtil.getCurrentSession().update(bo);
    }

    @Override
    public BO merge(BO bo) throws SQLException {
        return (BO) HibernateUtil.getCurrentSession().merge(bo);
    }

    @Override
    public Long createOrUpdate(BO bo) throws SQLException {
        HibernateUtil.getCurrentSession().saveOrUpdate(bo);
        return bo.getId();
    }

    /***
     * cascade incapable, when foreign referenc to id, an SQLException will be throw,
     * alternatively u can use delet(BO) which is cascade capable.
     * @param id
     * @throws SQLException
     */
    public void delete(Long id) throws SQLException {
//        try {
//            Session se = HibernateUtil.getCurrentSession();
//            Object o = se.get(Class.forName(getBOName()), id);
//            if(o != null) se.delete(0);
//        } catch (Exception ex) {
//            throw new SQLException(ex);
//        }
        Query qy = HibernateUtil.getCurrentSession().createQuery("delete " + this.getBOName() + " where id = " + id);
        qy.executeUpdate();
    }

    public void delete(BO bo) throws SQLException {
        HibernateUtil.getCurrentSession().delete(bo);
    }

    public void deleteAll() throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public BO get(Long id) throws SQLException {
        return (BO) HibernateUtil.getCurrentSession().get(this.getBOName(), id);
    }

    public BO getByCode(String code) throws SQLException {
        Query qy = HibernateUtil.getCurrentSession().createQuery(
                    ("from "+this.getBOName()+" as $ where $.code = (?)").replaceAll("\\$", this.getBOName().replaceAll("\\.", "_"))
                    );
        qy.setParameter(0, code);
        return (BO) qy.uniqueResult();
    }

    protected Query buildQueryForList(Filter filter, StringBuffer qstr, String shortname){
        List<Object[]> inparams = null;
        if (filter.getInOrNotIns() != null) {
            inparams = new ArrayList();
            int i = 0;
            for (Filter.InOrNotIn ini : filter.getInOrNotIns()) {
                qstr.append(" ");
                if (ini.isIn()) {
                    qstr.append(shortname + "." + ini.getField()+" in " + "(:_inParam"+ i +")");
                } else {
                    qstr.append(shortname + "." + ini.getField()+" not in " + "(:_inParam"+ i +")");
                }
                inparams.add(ini.getObjs());
            }

        }
        if (filter.getSorters() != null) {
            qstr.append(" order by ");
            for (Map.Entry<String, String> sorter : filter.getSorters()) {
                qstr.append(shortname + "." + sorter.getKey() + " " + sorter.getValue() + ",");
            }
            qstr.deleteCharAt(qstr.length() - 1);
        }
        Query qy = HibernateUtil.getCurrentSession().createQuery(qstr.toString());
        if (filter.getWhereParams() != null) {
            for (int i = 0; i < filter.getWhereParams().length; i++) {
                qy.setParameter(i, filter.getWhereParams()[i]);
            }
        }
        if(filter.getWhereParams_Named() != null && filter.getWhereParams_Named().size()>0){
            for(Map.Entry<String, Object> entry : filter.getWhereParams_Named().entrySet()){
                if(entry.getKey().startsWith("_list_")){
                    qy.setParameterList(entry.getKey(), (Collection)entry.getValue());
                }else{
                    qy.setParameter(entry.getKey(), entry.getValue());
                }
            }
        }
        if(inparams != null){
            for(int i = 0; i<inparams.size(); i++){
                qy.setParameterList("_inParam"+i, inparams.get(i));
            }
        }
        if (filter.getLimit() != null) {
            qy = qy.setMaxResults(filter.getLimit());
        }
        if (filter.getOffset() != null) {
            qy = qy.setFirstResult(filter.getOffset());
        }
        return qy;
    }

    public List listIds(Filter filter) throws SQLException {
        StringBuffer qstr;
        String shortname = this.getBOName().replaceAll("\\.", "_");
        qstr = new StringBuffer("select "+shortname+".id from " + this.getBOName() + " as " + shortname + " " + this.parseWhereString(filter));
        Query qy = buildQueryForList(filter, qstr, shortname);
        return qy.list();
    }

    public List<String> listCodes(Filter filter) throws SQLException {
        StringBuffer qstr;
        String shortname = this.getBOName().replaceAll("\\.", "_");
        qstr = new StringBuffer("select "+shortname+".code from " + this.getBOName() + " as " + shortname + " " + this.parseWhereString(filter));
        Query qy = buildQueryForList(filter, qstr, shortname);
        return qy.list();
    }

    public List list(Filter filter) throws SQLException {
        StringBuffer qstr;
        String shortname = this.getBOName().replaceAll("\\.", "_");
        qstr = new StringBuffer("from " + this.getBOName() + " as " + shortname + " " + this.parseWhereString(filter));
        Query qy = buildQueryForList(filter, qstr, shortname);
        return qy.list();
    }

    public Long count(Filter filter) throws SQLException {
        StringBuffer qstr;
        String shortname = this.getBOName().replaceAll("\\.", "_");
        qstr = new StringBuffer("select count(" + shortname + ".id) from " + this.getBOName() + " as " + shortname + " " + this.parseWhereString(filter));
        Query qy = buildQueryForList(filter, qstr, shortname);
        return (Long) qy.uniqueResult();
    }

    public List listAllCodes() throws SQLException {
        String shortname = this.getBOName().replaceAll("\\.", "_");
        String qystr = ("select code from "+this.getBOName()+" as $ "
                +" order by $.id"
                ).replaceAll("\\$", shortname);
        Query qy = HibernateUtil.getCurrentSession().createQuery(qystr);
        return qy.list();
    }

    public List listAllIds() throws SQLException {
        String shortname = this.getBOName().replaceAll("\\.", "_");
        String qystr = ("select id from "+this.getBOName()+" as $ "
                +" order by $.id"
                ).replaceAll("\\$", shortname);
        Query qy = HibernateUtil.getCurrentSession().createQuery(qystr);
        return qy.list();
    }

    /***
     * provide 'WHERE' condition for list and count method.
     * @param filter
     * @return a HQL String
     * @throws SQLException
     */
    protected String parseWhereString(Filter filter) throws SQLException {
        if ((filter.getWhereStr() != null && !filter.getWhereStr().isEmpty())
                || filter.getInOrNotIns() != null) {
            StringBuffer _where = new StringBuffer();
            _where.append(" where ");
            if(filter.getWhereStr()!=null) _where.append(filter.getWhereStr().replaceAll("\\$.", this.getBOName() + "."));
            return _where.toString();
        } else {
            return "";
        }
    }

    public void flush() throws SQLException {
        HibernateUtil.getCurrentSession().flush();
    }
    
    public void clear() throws SQLException {
        HibernateUtil.getCurrentSession().clear();
    }

    public static void startTransaction() throws SQLException {
        HibernateUtil.startTransaction();
    }

    public static void commitTransaction() throws SQLException {
        HibernateUtil.commitTransaction();
    }

    public static void rollbackTransaction() throws SQLException {
        HibernateUtil.rollbackTransaction();
    }

    /**
     * check if given field exists
     * @param fieldname
     * @param value
     * @return BO instance if it exists otherwise returns null
     */
    public Object checkUniqueField(String fieldname, Object value){
        String shortname = this.getBOName().replaceAll("\\.", "_");
        String whereStr = ("from " + this.getBOName() + " as $ where "
               +"$."+fieldname+"=(:_value)"
                )
                .replaceAll("\\$", shortname);
        Map<String, Object> whereParams_named = new HashMap<String, Object>();

        whereParams_named.put("_value", value);
        Query qy = HibernateUtil.getCurrentSession().createQuery(whereStr);
        for (Map.Entry<String, Object> entry : whereParams_named.entrySet()) {
            if (entry.getKey().startsWith("_list_")) {
                qy.setParameterList(entry.getKey(), (Collection) entry.getValue());
            } else {
                qy.setParameter(entry.getKey(), entry.getValue());
            }
        }
        qy.setMaxResults(1);
        qy.setFirstResult(0);
        List re = (List) qy.list();
        if(re!=null && re.size()>0){
            return re.get(0);
        }else{
            return null;
        }

    }
}
